﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace StructConver
{
    public class ConverC
    {
        static StringBuilder sbd = new StringBuilder();
        static StringBuilder sbd2 = new StringBuilder();
        static Dictionary<string, string> mapConst = new Dictionary<string, string>();
        static int pack = 0;

        public static void CountConstValue(string fileName)
        {
            //1开始读取 声明的常量;
            string pathRoot = System.Environment.CurrentDirectory;
            pathRoot=pathRoot.Replace("\\bin\\Debug", string.Empty);
            string path = pathRoot + "\\" + fileName + ".h";
            StreamReader file = new StreamReader(path, Encoding.Default);
            string line;
            string tmp;
            while ((line = file.ReadLine()) != null)
            {
                tmp = ConverConstField(line);
            }
        }

        /// <summary>
        /// 把C++脚本转为CS脚本
        /// </summary>
        /// <param name="fileName"></param>
        public static void ConverCStruct(string fileName)
        {
            CountConstValue(fileName);
            sbd.Clear();
            sbd.AppendLine("using System;");
            sbd.AppendLine("using System.Runtime.InteropServices;");
            sbd.AppendLine("public class "+ fileName);
            sbd.AppendLine("{");
            Regex regex = new Regex(@"(?i)(?<=\[)(.*)(?=\])");//中括号[]
            //1开始读取 声明的常量;
            string pathRoot = System.Environment.CurrentDirectory;
            pathRoot = pathRoot.Replace("\\bin\\Debug", string.Empty);
            string path = pathRoot + "\\" + fileName+".h";
            StreamReader file = new StreamReader(path, Encoding.Default);
            string line;
            string tmp;
            string size = null;
            bool isEnum = false;
            bool isStruct = false;
            bool isArray = false;
            while ((line = file.ReadLine() )!=null)
            {
                tmp = ConverConstField(line);
                if (!string.IsNullOrEmpty(tmp))
                {   //常量类型;
                    sbd.AppendLine("\t"+tmp);
                }
                else if (line.Contains("enum") )
                {   //枚举类型;
                    sbd.AppendLine("\t"+line);
                    isEnum = true;
                }
                else if (string.IsNullOrEmpty(tmp) && !string.IsNullOrEmpty(line) && line.Contains("struct"))
                {   //结构体类型;
                    isEnum = false;
                    isStruct = true;
                    //////////////////////////
                    sbd2.Clear();
                    sbd2.AppendLine("[StructLayout(LayoutKind.Sequential, Pack = {@})]");
                    sbd2.AppendLine(line.Replace("struct", "\tpublic struct "));
                    if (line.Contains("CMD_Head"))
                    {
                        int a = 10;
                    }
                }
                else if ( !string.IsNullOrEmpty(line) && !isEnum && isStruct)
                {   //结构体内字段;
                    if (regex.IsMatch(line))
                    {
                        var matchs = regex.Matches(line);
                        size = null;
                        foreach (var s in matchs)
                        {
                            isArray = true;
                            if (s.ToString().Contains("]["))
                            {   //数组类型声明;
                                string[] sps = s.ToString().Replace("][", "*").Split('*');
                                for (int i = 0; i < sps.Length; i++)
                                {
                                    size += mapConst.ContainsKey(sps[i].Trim())? mapConst[sps[i].Trim()] : sps[i].Trim();
                                    if (i < sps.Length - 1)
                                    {
                                        size += "*";
                                    }
                                }
                                sbd2.AppendLine("\t\t" + string.Format("[MarshalAs(UnmanagedType.ByValArray, SizeConst = {0})]", size));
                            }
                            else if (mapConst.ContainsKey(s.ToString().Trim()))
                            {
                                size = mapConst[s.ToString().Trim()];
                                sbd2.AppendLine("\t\t" + string.Format("[MarshalAs(UnmanagedType.ByValArray, SizeConst = {0})]", size));
                            }
                            else if (s.ToString().Contains("*"))
                            {   //乘法类型声明;
                                isArray = true;
                                if (s.ToString().Contains("*"))
                                {
                                    string[] sps = s.ToString().Split('*');
                                    for (int i = 0; i < sps.Length; i++)
                                    {
                                        size += mapConst.ContainsKey(sps[i].Trim())? mapConst[sps[i].Trim()] : sps[i].Trim();
                                        if (i < sps.Length - 1)
                                        {
                                            size += "*";
                                        }       
                                    }
                                }
                                sbd2.AppendLine("\t\t" + string.Format("[MarshalAs(UnmanagedType.ByValArray, SizeConst = {0})]", size));
                            }
                            else
                            {
                                sbd2.AppendLine("\t\t"+string.Format("[MarshalAs(UnmanagedType.ByValArray, SizeConst = {0})]", s));
                            }
                            
                            line = line.Replace("[" + s.ToString() + "]", string.Empty);
                        }
                    }
                    tmp = ConverStructField(line, isArray);
                    sbd2.AppendLine("\t"+tmp);
                    isArray = false;
                    if (line.Contains("}"))
                    {   //本次结构体结束;
                        isStruct = false;
                        //处理结构体;
                        if (sbd2.Length > 10)
                        {
                            string str ="\t"+sbd2.ToString();
                            sbd.AppendLine(str.Replace("{@}", pack.ToString()));
                            pack = 0;
                            sbd2.Clear();
                        }
                    }

                }
                else if(!string.IsNullOrEmpty(line) && !isStruct && !isEnum)
                {   //;
                    sbd.AppendLine("\t//"+line);
                }
                else 
                {
                    sbd.AppendLine("\t"+line);
                }
                tmp = null;
            }
            sbd.AppendLine("}");
            path = CreateCSFile(sbd.ToString(), fileName);
            Console.WriteLine("文件输出路径=" + path);

        }

        static string ConverConstField(string strConst)
        {
            string res = null;
            if (strConst.Contains("#define"))
            {
                string[] sps = strConst.Replace("\t", " ").Split(' ');
                if (sps.Length<3)
                {
                    return res;
                }
                string tmp = "";
                int addCount = 0;
                string key=null;
                for(int i=0; i < sps.Length; i++)
                {
                    tmp = sps[i];
 
                    if (string.IsNullOrEmpty(tmp))
                    {
                        continue;
                    }
                    if (tmp=="#define")
                    {
                        res += "public const ushort ";
                        addCount++;
                        continue;
                    }
                    else if (addCount == 1)
                    {
                        key = sps[i].Trim();
                        res += sps[i];
                        res += "= ";
                        addCount++;
                        continue;
                    }
                    else if (addCount == 2)
                    {
                        res += sps[i];
                        res += ";\t\t";
                        addCount++;
                        if (!mapConst.ContainsKey(key))
                        {
                            mapConst.Add(key, sps[i].Trim());
                        }
                        
                        key = null;
                        continue;
                    }
                    else
                    {
                        res += sps[i];
                    }
                }
            }
            return res;
        }


        //1,2,4,8,16,32,64,128...
        static string ConverStructField(string filed, bool isArray)
        {
            if (filed.Contains("DWORD"))
            {
                pack = pack< 4?4:pack;
                return filed.Replace("DWORD", isArray ? "public UInt32[]" : "public UInt32");
            }
            else if (filed.Contains("TCHAR"))
            {
                pack = pack<1?1:pack;
                return filed.Replace("TCHAR", isArray ? "public byte[]" : "public byte");
            }
            else if (filed.Contains("WORD"))
            {
                pack = pack<2?2:pack;
                return filed.Replace("WORD", isArray ? "public UInt16[]" : "public UInt16");
            }
            else if (filed.Contains("UINT"))
            {
                pack = pack<4?4:pack;
                return filed.Replace("UINT", isArray ? "public UInt32[]" : "public UInt32 ");
            }
            else if (filed.Contains("ULONGLONG"))
            {
                pack = pack<8?8:pack;
                return filed.Replace("ULONGLONG", isArray ? "public UInt64[]" : "public UInt64");
            }
            else if (filed.Contains("LONGLONG"))
            {
                pack = pack < 8 ? 8 : pack;
                return filed.Replace("LONGLONG", isArray ? "public Int64[]" : "public Int64");
            }
            else if (filed.Contains("LONG"))
            {
                pack = pack<4?4:pack;
                return filed.Replace("LONG", isArray ? "public Int32[]" : "public Int32 ");
            }
            else if (filed.Contains("BYTE"))
            {
                pack = pack<1?1:pack;
                return filed.Replace("BYTE", isArray ? "public byte[]" : "public byte ");
            }
            else if (filed.Contains("BOOL"))
            {
                pack = pack<4?4:pack;
                return filed.Replace("BOOL", isArray ? "public Int32[]" : "public Int32 ");
            }
            else if (filed.Contains("bool"))
            {
                pack = pack<1?1:pack;
                return filed.Replace("bool", isArray ? "public byte[]" : "public byte ");
            }
            else if (filed.Contains("float"))
            {
                pack = pack < 8 ? 8 : pack;
                return filed.Replace("float", isArray ? "public float[]" : "public float ");
            }
            else if (filed.Contains("uint8_t"))
            {
                pack = pack < 1 ? 1 : pack;
                return filed.Replace("uint8_t", isArray ? "public byte[]" : "public byte ");
            }
            else if (filed.Contains("\tuint16_t\t"))
            {
                pack = pack < 2 ? 2 : pack;
                return filed.Replace("\tuint16_t\t", isArray ? "public UInt16[]" : "public UInt16 ");
            }
            else if (filed.Contains("\tuint32_t\t"))
            {
                pack = pack < 4 ? 4 : pack;
                return filed.Replace("\tuint32_t\t", isArray ? "public UInt32[]" : "public UInt32 ");
            }
            else if (filed.Contains("\tuint64_t\t"))
            {
                pack = pack < 8 ? 8 : pack;
                return filed.Replace("\tuint64_t\t", isArray ? "public UInt64[]" : "public UInt64 ");
            }
            else if (filed.Contains("\tint16_t\t"))
            {
                pack = pack < 2 ? 2 : pack;
                return filed.Replace("\tint16_t\t", isArray ? "public Int16[]" : "public Int16 ");
            }
            else if (filed.Contains("\tint32_t\t"))
            {
                pack = pack < 4 ? 4 : pack;
                return filed.Replace("\tint32_t\t", isArray ? "public Int32[]" : "public Int32 ");
            }
            else if (filed.Contains("\tint64_t\t"))
            {
                pack = pack < 8 ? 8 : pack;
                return filed.Replace("\tint64_t\t", isArray ? "public Int64[]" : "public Int64 ");
            }
            else if (filed.Contains("__int64"))
            {
                pack = pack<8?8:pack;
                return filed.Replace("__int64", isArray ? "public Int64[]" : "public Int64 ");
            }
            else if (filed.Contains("INT64"))
            {
                pack = pack < 8 ? 8 : pack;
                return filed.Replace("INT64", isArray ? "public Int64[]" : "public Int64 ");
            }
            else if (filed.Contains("time_t"))
            {
                pack = pack < 8 ? 8 : pack;
                return filed.Replace("time_t", isArray ? "public UInt64[]" : "public UInt64 ");
            }
            else if (filed.Contains("__time64_t"))
            {
                pack = pack<8?8:pack;
                return filed.Replace("__time64_t", isArray ? "public Int64[]" : "public Int64 ");
            }
            else if (filed.Contains("INT32"))
            {
                pack = pack<4?4:pack;
                return filed.Replace("INT32", isArray ? "public Int32[]" : "public Int32 ");
            }
            else if (filed.Contains("WCHAR"))
            {
                pack = pack<4?4:pack;
                return filed.Replace("WCHAR", isArray ? "public UInt16[]" : "public UInt16 ");
            }
            else if (filed.Contains("char"))
            {
                pack = pack<4?4:pack;
                return filed.Replace("char", isArray ? "public byte[]" : "public byte ");
            }
            else if (filed.Contains("\tint\t"))
            {
                pack = pack < 4 ? 4 : pack;
                return filed.Replace("\tint\t", isArray ? "public Int32[]" : "public Int32 ");
            }
            else if (!filed.Contains("{") && !filed.Contains("}") && !filed.Replace("\t",string.Empty).Trim().StartsWith("//"))
            {
                if (string.IsNullOrEmpty( filed.Replace("\t",string.Empty).Trim()) )
                {
                    return filed;
                }
                string[] sps = filed.Replace("\t", " ").Split(' ');
                filed = "\tpublic\t";
                int addCount = 0;
                foreach (string tmp in sps)
                {
                    if (!string.IsNullOrEmpty(tmp))
                    {
                        addCount++;
                        filed +="\t"+ tmp;
                        if (addCount==1)
                        {
                            filed += isArray ? "[]" : "";
                        }
                    }
                }
                return filed;
            }
            else
                return filed;
        }


        static string CreateCSFile(string content, string fileName)
        {
            string pathRoot = System.Environment.CurrentDirectory;
            string path = pathRoot + "\\" + fileName + ".cs";
            Console.WriteLine(content);
            using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                using (TextWriter textWriter = new StreamWriter(fileStream, Encoding.UTF8))
                {
                    textWriter.Write(content);
                }
            }
            return path;
        }


    }
}
